#pragma once

#include <iostream>
#include <stdarg.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

enum level
{
    Debug = 0, // 测试
    Normal,    // 正常的
    Warning,   // 有错误，但是不影响运行
    Error,     // 严重错误，终止运行
};

enum OpenMethod
{
    Screen = 0,
    OneFile,
    MultipleFile,
};

std::string LevelToString(int level)
{
    switch (level)
    {
    case Debug:
        return "Debug";
    case Normal:
        return "Normal";
    case Warning:
        return "Warning";
    case Error:
        return "Error";
    default:
        return "Don't Know";
    }
}

std::string GetNowTime()
{
    time_t t = time(nullptr);
    struct tm *pt;
    pt = localtime(&t);
    char buffer[1024];
    sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d", pt->tm_year + 1900, pt->tm_mon + 1, pt->tm_mday,
            pt->tm_hour, pt->tm_min, pt->tm_sec);
    return buffer;
}

const char *dir = "./LogDir";
const char *file = "/log.";

class Log
{
public:
    Log(int open_method = Screen)
    {
        ChangeOpenMethod(open_method);
    }

    static void ChangeOpenMethod(int open_method)
    {
        if (open_method == Screen || open_method == OneFile || open_method == MultipleFile)
            _method = open_method;
        else
            std::cout << "Methods that do not exist are used, Please try again" << std::endl;
    }

    static void LogMessage(int level, const char *format, ...)
    {
        if (_method == -1)
        {
            std::cout << "Methods that do not exist are used, Please change method" << std::endl;
            return;
        }

        va_list sa;
        va_start(sa, format);
        char buffer[1024];
        vsnprintf(buffer, sizeof(buffer), format, sa);
        va_end(sa);

        std::string leval_str = LevelToString(level);
        std::string now_time = GetNowTime();
        pid_t id = getpid();

        char message[1024];
        snprintf(message, sizeof(message), "[%s][%d][%s] [%s]\n", leval_str.c_str(), id, now_time.c_str(), buffer);

        switch (_method)
        {
        case Screen:
            std::cout << message;
            break;
        case OneFile:
            WriteToFile("all", message);
            break;
        case MultipleFile:
            WriteToFile(leval_str, message);
            break;
        default:
            break;
        }
    }

private:
    static void WriteToFile(const std::string &leval, const std::string &message)
    {
        mkdir(dir, 0777);
        std::string file_name = dir;
        file_name += file;
        file_name += leval;

        int fd = open(file_name.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
        if (fd < 0)
        {
            std::cout << "open file error" << std::endl;
            return;
        }

        int n = write(fd, message.c_str(), message.size());
        if (n < 0)
        {
            std::cout << "open file error" << std::endl;
            return;
        }

        close(fd);
    }

    static int _method;
};

int Log::_method = 0;

static Log lg;